package org.summerframework.component.security.core.filter;


import org.summerframework.component.security.LoginData;
import org.summerframework.component.security.context.RequestContextFactory;
import org.summerframework.component.security.core.RequestParamHandle;
import org.summerframework.component.security.core.interceptor.RequestContextInterceptor;
import org.summerframework.core.util.AnnotationUtils;
import org.summerframework.core.util.HandlerMethodUtil;
import org.summerframework.web.filter.BaseFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 石超
 * @version v1.0.0
 */
@Order(Integer.MIN_VALUE + 51)
public class RequestParamFilter implements BaseFilter {
    private RequestContextInterceptor requestContextInterceptor;

    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Autowired(required = false)
    private List<RequestParamHandle> requestParamHandles;

    public RequestParamFilter(RequestMappingHandlerMapping requestMappingHandlerMapping, RequestContextInterceptor requestContextInterceptor) {
        this.requestMappingHandlerMapping = requestMappingHandlerMapping;
        this.requestContextInterceptor = requestContextInterceptor;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            onFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
        } catch (Exception e) {
            failure(request, response, e);
        } finally {
            RequestContextFactory.clean();
        }
    }

    private void onFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            HandlerExecutionChain handlerMethod = HandlerMethodUtil.getHandlerMethod(requestMappingHandlerMapping, request);

            requestContextInterceptor.preHandle(request, response, handlerMethod == null ? null : handlerMethod.getHandler());

            if (handlerMethod != null) {
                if (handlerMethod.getHandler() instanceof HandlerMethod) {
                    chain.doFilter(new ParamRequestWrapper(request, (HandlerMethod) handlerMethod.getHandler(), RequestContextFactory.getLoginData()), response);
                    return;
                }
            }

            chain.doFilter(request, response);
        } finally {
            try {
                requestContextInterceptor.afterCompletion(request, response, null, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private class ParamRequestWrapper extends HttpServletRequestWrapper {
        private Map<String, String[]> parameter;

        /**
         * Constructs a request object wrapping the given request.
         *
         * @param request the {@link HttpServletRequest} to be wrapped.
         * @throws IllegalArgumentException if the request is null
         */
        ParamRequestWrapper(HttpServletRequest request, HandlerMethod handlerMethod, LoginData<?> loginData) {
            super(request);
            this.parameter = new ConcurrentHashMap<>(request.getParameterMap());

            if (!loginData.isLogin() || requestParamHandles == null) {
                return;
            }

            for (RequestParamHandle<Annotation> requestParamHandle : requestParamHandles) {
                final Annotation annotation = AnnotationUtils.findAnnotation(handlerMethod, requestParamHandle.getAnnotation());

                if (annotation == null) continue;

                requestParamHandle.doRequest(annotation, this, loginData);
            }
        }

        @Override
        public String getParameter(String name) {
            String[] parameterValues = getParameterValues(name);

            if (parameterValues != null && parameterValues.length > 0) {
                return parameterValues[0];
            }

            return null;
        }

        @Override
        public Map<String, String[]> getParameterMap() {
            return parameter;
        }

        @Override
        public Enumeration<String> getParameterNames() {
            return Collections.enumeration(parameter.keySet());
        }

        @Override
        public String[] getParameterValues(String name) {
            return parameter.get(name);
        }
    }
}
